﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Threading;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusCompView
{
    public partial class FinPlusGroupGrid : FinPlusControl
    {
        [State(Test = true)]
        public override bool IsValid { get; set; }
        [State(Save = true)]
        public new double Height { get { return base.Height; } set { base.Height = value; } }
        [State(Save = true)]
        public new double Width { get { return base.Width; } set { base.Width = value; } }
        [State(Test = true)]
        public override string TestString { get { return _adapter.ToString(); } }
        [State(Test = true)]
        public string EditToString { get { return GetEditToString(); } }
        [State(Test = true)]
        public string ViewToString { get { return GetViewToString(); } }
        [State(Test = true)]
        public int ItemsCount { get { return dataGrid.Items.Count; } }
        [State(Save = true)]
        public string Filterbars { get { return _filterbarTray.Filterbars; } set { _filterbarTray.Filterbars = value; HeaderCheckBoxFilterbars(); } }
        [State(Save = true)]
        public string FilteredOut { get { return _filterbarTray.FilteredOut; } set { _filterbarTray.FilteredOut = value; } }
        [State(Save=true, Test=true)]
        public string ColumnState { get { return _colHelper.GetColState(dataGrid); } set { _colHelper.SetColState(dataGrid, _colSelectBar, value); } }
        public override IPods Adapter { get { return _adapter; } }
        public IDPods DPods { get; private set; }

        private enum Controls { FinPlusConnectService, FinPlusConnectXML, FinPlusDPodStub, FinPlusDPodLogger, FinPlusFilterbarTray, FinPlusTransform, FinPlusColSelectBar, FinPlusColOnOffBar, FinPlusColButtonBar, FinPlusColValueShiftBar, FinPlusContextMenu, FinPlusMenuBar, DPods, Default }
        private CollectionViewSource _viewSource; 
        private FinPlusStatusBar _statusBar;
        private IFinPlusConnect _conn;
        private FinPlusFilterbarTray _filterbarTray;
        private FinPlusColValueShiftBar _colValueShiftBar;
        private FinPlusColSelectBar _colSelectBar;
        private FinPlusColButtonBar _buttonBar;
        private FinPlusColOnOffBar _colOnOffBar;
        private FinPlusContextMenu _contextMenu;
        private FinPlusMenuBar _menuBar;
        private readonly FinPlusGridColHelper _colHelper;
        private readonly IPods _adapter;
 
        //construct
        public FinPlusGroupGrid(object[] links, IFinPlusComp colHelper, bool makeRowVirtual, bool makeColVirtual)
        {
            InitializeComponent();
            _adapter = IOC.New<IPods>();

            dataGrid.EnableRowVirtualization = makeRowVirtual;
            dataGrid.EnableColumnVirtualization = makeColVirtual;

            _colHelper = (FinPlusGridColHelper)colHelper;
            _colHelper.Link(this, Filterbar_Add, Filterbar_Remove);

            if (links != null)
                foreach (var link in links)
                    Link(link, string.Empty);

            IsValid = true;
        }

        //common control interface
        public override void CmdExecute(IPod command)
        {
            try
            {
                CmdCapture(command);

                switch ((Cmds)Enum.Parse(typeof(Cmds), command.Name, true))
                {
                    case Cmds.Refresh: _viewSource.View.Refresh(); break;
                    case Cmds.AddFilterbar: AddFilterbar(command["Tag"].ToString()); break;
                    case Cmds.RemoveFilterbar: RemoveFilterbar(command["Tag"].ToString()); break;
                    case Cmds.ColSelection: AddColSelection(command["Tag"].ToString()); break;
                    case Cmds.ColOn: ColOnOff(command, true); break;
                    case Cmds.ColOff: ColOnOff(command, false); break;
                    case Cmds.ColButton: ColButton(command); break;
                    case Cmds.ColValueShiftUp: ColValueShift(command, true); break;
                    case Cmds.ColValueShiftDown: ColValueShift(command, false); break;
                    case Cmds.ColValShiftLevel: ColValueShiftLevel(command); break;
                    case Cmds.NewColumn: AddColumn(command["Tag"].ToString()); break;
                    default: throw new Exception(string.Format("cmd not recognised {0}", command.Name));
                }
            }
            catch (CmdExecuteException e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        public override void Dispose()
        {
            try
            {
                if(_conn != null)
                    _conn.Dispose();
                if (_colHelper != null)
                    _colHelper.Dispose();
            }
            catch (Exception e)
            {
                Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        //public
        public void AddPod(IDPod value)
        {
            try
            {
                DPods = IOC.New<IDPods>();
                DPods.Add(value);
                _viewSource = new CollectionViewSource() { Source = DPods };
                dataGrid.DataContext = _viewSource.View;
                dataGrid.IsEnabled = false;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        //private 
        private void Link(object control, string dock)
        {
            try
            {
                switch (control.GetType().Name.EnumParse<Controls>(Controls.Default))
                {
                    case Controls.FinPlusDPodStub:
                    case Controls.FinPlusDPodLogger:
                    case Controls.FinPlusConnectXML:
                    case Controls.FinPlusConnectService: AddConnection((IFinPlusConnect)control); break;
                    case Controls.FinPlusFilterbarTray: AddFilterbarTray((FinPlusFilterbarTray)control, dock); break;
                    case Controls.FinPlusColSelectBar: AddColSelector((FinPlusColSelectBar)control, dock); break;
                    case Controls.FinPlusColOnOffBar: AddColOnOff((FinPlusColOnOffBar)control, dock); break;
                    case Controls.FinPlusColButtonBar: AddButtonBar((FinPlusColButtonBar)control, dock); break;
                    case Controls.FinPlusColValueShiftBar: AddColValueShift((FinPlusColValueShiftBar)control, dock); break;
                    case Controls.FinPlusTransform: AddTransform((FinPlusTransform)control); break;
                    case Controls.FinPlusContextMenu: AddContextMenu((FinPlusContextMenu)control); break;
                    case Controls.FinPlusMenuBar: AddMenuBar((FinPlusMenuBar)control); break;
                    case Controls.DPods: AddDPods((IDPods)control); break;
                    case Controls.Default: toolbarTray.Children.Add(DockSet((UserControl)control, dock)); break;
                }
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private FinPlusStatusBar FindSatusbar()
        {
            if (_statusBar == null)
            {
                var app = this.FindVisualParent<FinPlusWindow>();
                if (app != null)
                    return _statusBar = app.FindChild<FinPlusStatusBar>();
                
            }
            return _statusBar;
        }

        private void AddConnection(IFinPlusConnect conn)
        {
            try
            {
                _conn = conn;
                DPods = conn.DPods();
                _viewSource = new CollectionViewSource() { Source = DPods };
                dataGrid.ItemsSource = _viewSource.View;
                dataGrid.IsEnabled = _conn.CanWrite;
                DPods.ColumnChanged += Column_New;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddDPods(IDPods dPods)
        {
            try
            {
                _viewSource = new CollectionViewSource() { Source = DPods = dPods };
                dataGrid.ItemsSource = _viewSource.View;
                dataGrid.IsEnabled = true;
                DPods.ColumnChanged += Column_New;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddColSelector(FinPlusColSelectBar colSelector, string dock)
        {
            try
            {
                _colSelectBar = colSelector;
                _colSelectBar.ColSelection += ColSelector_OnSelection;
                var value = IOC.New<IPod>();
                value.Object = colSelector;
                _adapter.Add(value, true, false);
                buttonbarTray.Children.Add(DockSet(_colSelectBar, dock));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddColOnOff(FinPlusColOnOffBar colOnOff, string dock)
        {
            try
            {
                _colOnOffBar = colOnOff;
                _colOnOffBar.ColOnOff += ColOnOff_Click;
                var value = IOC.New<IPod>();
                value.Object = colOnOff;
                _adapter.Add(value, true, false);
                buttonbarTray.Children.Add(DockSet(_colOnOffBar, dock));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddButtonBar(FinPlusColButtonBar buttonBar, string dock)
        {
            try
            {
                _buttonBar = buttonBar;
                _buttonBar.ColButtonClick += ColButton_Click;
                buttonbarTray.Children.Add(DockSet(buttonBar, dock));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddColValueShift(FinPlusColValueShiftBar colValueShift, string dock)
        {
            try
            {
                _colValueShiftBar = colValueShift;
                _colValueShiftBar.ColValueShift += ColValueShift_Click;
                var value = IOC.New<IPod>();
                value.Object = colValueShift;
                _adapter.Add(value, true, false);
                buttonbarTray.Children.Add(DockSet(_colValueShiftBar, dock));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddFilterbarTray(FinPlusFilterbarTray filterbarTray, string dock)
        {
            try
            {
                using (_viewSource.View.DeferRefresh())
                {
                    _filterbarTray = filterbarTray;
                    if (_viewSource == null)
                        throw new Exception("Filtertray has no viewsource");
                    _filterbarTray.AddConnection(DPods);
                    _filterbarTray.FilterUpdate += View_Refresh;
                    _viewSource.View.Filter = obj => { return Filter(obj); };
                }
                toolbarTray.Children.Add(DockSet(_filterbarTray, dock));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddTransform(FinPlusTransform transform)
        {
            buttonbarTray.Children.Add(transform);
        }

        private void AddContextMenu(FinPlusContextMenu contextMenu)
        {
            _contextMenu = contextMenu;
            dataGrid.ContextMenu = contextMenu.Menu;
        }

        private void AddMenuBar(FinPlusMenuBar menuBar)
        {
            _menuBar = menuBar;
            buttonbarTray.Children.Add(menuBar);
        }

        private bool AddFilterbar(string name)
        {
            using (_viewSource.View.DeferRefresh())
            {
                if (_filterbarTray.HasFilterbar(name)) 
                    return false;

                _filterbarTray.AddFilterbar(new FinPlusFilterbar(name));
                var value = IOC.New<IPod>();
                value.Object = new FinPlusFilterbar(name);
                
                _adapter.Add(value, true, false);
            }

            return true;
        }

        private void AddColumns()
        {
            try
            {
                using (_viewSource.View.DeferRefresh())
                    foreach (var dPod in DPods)
                        foreach (var name in dPod.GetDynamicMemberNames())
                            AddColumn(name, dPod.GetType(name));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddColumn(string name, string type = "ValueFormat")
        {
            var col = _colHelper.GetTemplate(name, type);
            if (col != null)
                dataGrid.Columns.Add(col);
        }

        private void AddColSelection(string name)
        {
            var col = dataGrid.Columns.Where(c => c.GetValue(TagProperty).ToString().Equals(name)).First();
            col.Visibility = col.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
        }

        private void ColOnOff(IPod value, bool onOff)
        {
            try
            {
                var colName = value["Tag"].ToString();

                foreach (var dPod in DPods)
                    if (Filter(dPod) && Permissions.Get(dPod).CanWrite && !dPod.GetValue(colName).Equals(onOff))
                        dPod.SetValue(colName, onOff, User, DateTime.Now);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void ColButton(IPod value)
        {
            var s = value["Tag"].ToString().Split(',');
            DPods[0].SetValue(s[1], IOC.New<IPod>(s[0]).ToString(), User, DateTime.Now);
        }

        private void ColValueShift(IPod value, bool up)
        {
            try
            {
                var shift = value["Tag"].ToString().Split(',');

                foreach (var dPod in DPods)
                {
                    if (!Filter(dPod)) 
                        continue;

                    double shiftColVal, factor, minShift;
                    if (Permissions.Get(dPod).CanWrite && double.TryParse(dPod.GetValue(shift[0]).ToString(), out shiftColVal) && double.TryParse(shift[1], out factor) && double.TryParse(dPod.GetValue(shift[2]).ToString(), out minShift))
                        dPod.SetValue(shift[0], shiftColVal + (up ? factor : -factor) * minShift, User, DateTime.Now);
                }
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void ColValueShiftLevel(IPod value)
        {
            try
            {
                var shift = value["Tag"].ToString().Split(',');

                foreach (var dPod in _conn.DPods())
                    if (Filter(dPod) && Permissions.Get(dPod).CanWrite && !dPod.GetValue(shift[0]).Equals(0.0))
                        dPod.SetValue(shift[0], 0.0, User, DateTime.Now);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void RemoveFilterbar(string name)
        {
            _filterbarTray.RemoveFilterbar(name);
        }

        private bool Filter(object obj)
        {
            if (obj == null)
                return false;

            var pod = (IDPod)obj;
            foreach (KeyValuePair<string, FinPlusFilterbar> name in _filterbarTray.FilterOut)
                foreach (var value in name.Value.FilterOut)
                    if (pod.HasObject(name.Key, value))
                        return false;

            return true;
        }

        private void HeaderCheckBoxFilterbars()
        {
            foreach (var kvp in _colHelper.ColMaps)
                if (_filterbarTray != null)
                    kvp.Value.FilterCheck.IsChecked = _filterbarTray.HasFilterbar(kvp.Key);
        }

        private string GetViewToString()
        {
            var s = new StringBuilder();
            s.Append("{");
            for (int row = 0; row < dataGrid.Items.Count; row++)
            {
                s.Append("{");
                for (int col = 0; col < dataGrid.Columns.Count; col++)
                {
                    var cell = dataGrid.GetCell(row, col);
                    var textBlock  =cell.FindChild<TextBlock>();
                    if (textBlock != null)
                        s.Append(textBlock.Text);
                    if (col < dataGrid.Columns.Count - 1)
                        s.Append(",");
                }
                s.Append("}");
                if (row < dataGrid.Items.Count - 1)
                    s.Append(",");
            }
            s.Append("}");
            return s.ToString();
        }

        private string GetEditToString()
        {
            var s = new StringBuilder();
            s.Append("{");
            for (int row = 0; row < dataGrid.Items.Count; row++)
            {
                s.Append("{");
                for (int col = 0; col < dataGrid.Columns.Count; col++)
                {
                    var cell = dataGrid.GetCell(row, col);
                    var textBlock = cell.FindChild<TextBlock>();
                    if (textBlock != null)
                        s.Append(textBlock.Text);
                    if (col < dataGrid.Columns.Count - 1)
                        s.Append(",");
                }
                s.Append("}");
                if (row < dataGrid.Items.Count - 1)
                    s.Append(",");
            }
            s.Append("}");
            return s.ToString();
        }

        //cmds
        private enum Cmds { Refresh, AddFilterbar, NewColumn, RemoveFilterbar, ColSelection, ColOn, ColOff, ColButton, ColValueShiftUp, ColValueShiftDown, ColValShiftLevel }

        //events
        private void View_Refresh(object s, IPod pod) { CmdExecute(pod); }
        private void ColSelector_OnSelection(object s, IPod pod) { CmdExecute(pod); }
        private void ColOnOff_Click(object s, IPod pod) { CmdExecute(pod); }
        private void ColButton_Click(object s, IPod pod) { CmdExecute(pod); }
        private void ColValueShift_Click(object s, IPod pod) { CmdExecute(pod); } 
        private void Filterbar_Add(object s, RoutedEventArgs a) { CmdRouter(Cmds.AddFilterbar.ToString(), s); }
        private void Filterbar_Remove(object s, RoutedEventArgs a) { CmdRouter(Cmds.RemoveFilterbar.ToString(), s); }
        private void Lost_Focus(object s, RoutedEventArgs a) { dataGrid.SelectedIndex = -1; }
        private void Grid_Selected(object s, RoutedEventArgs a)
        {
            try
            {
                if (a.OriginalSource.GetType() != typeof(DataGridCell)) 
                    return;

                dataGrid.BeginEdit(a);

                if (FindSatusbar() != null)
                    _statusBar.Update(dataGrid);
            }
            catch (Exception e)
            {
                Level.Error.Log(Id, Config, e.Message, e);
            }
        }
        private void Column_New(object s, PropertyChangedEventArgs a) 
        {
            if (!CheckAccess())
               Dispatcher.Invoke(() => CmdRouter(Cmds.NewColumn.ToString(),a.PropertyName));
            else
               CmdRouter(Cmds.NewColumn.ToString(),a.PropertyName);
        }
   }
}